//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Web;
using System.Xml;
using CommunityServer.Components;

namespace CommunityServer.Spam
{
	/// <summary>
	/// Summary description for SpamRunner.
	/// </summary>
	public class SpamRunner
	{
		private int autoModerate = 0;
		private int autoDelete = 0;
		private bool audit = false;
		private bool log = false;

		public SpamRunner(int autoModerate, int autoDelete, bool audit, bool log)
		{
			this.autoModerate = autoModerate;
			this.autoDelete = autoDelete;
			this.audit = audit;
			this.log = log;
		}

		public int RatePost(Post post, CSPostEventArgs e)
		{
			// Initialize spam score
			int score = 0;

			// Get configured rules
			ArrayList rules = SpamRules.GetSelectedRules();

			// Perform multiple passes over rules and rate for spam
			for (int pass = 1; pass <= 3; pass++)
			{
				// Loop thru each rule
				foreach (SpamRule rule in rules)
				{
					// Run rule against post
					int ruleScore = RunRule(pass, score, rule, post, e);

					// Add rule score to total score (handle overflow/underflow)
					score = Add(score, ruleScore);
				}
			}

			// Save spam score
			post.SpamScore = score;

			// Is post definite spam?
			if (score >= autoDelete)
			{
				post.PostStatus |= PostStatus.Spam;
				post.IsApproved = false;
				if (log)
					Log(post, score, e.ApplicationType, false, audit);
			}
			// Is post likely spam?
			else if (score >= autoModerate)
			{
				post.PostStatus |= PostStatus.LikelySpam;
				post.IsApproved = false;
				if (log)
					Log(post, score, e.ApplicationType, true, audit);
			}

			return score;
		}

		private int RunRule(int passIndex, int postScore, SpamRule rule, Post post, CSPostEventArgs e)
		{
			int ruleScore = 0;

			if (((passIndex == 1) && RunOnFirstPass(rule)) ||
				((passIndex == 2) && RunOnSecondPass(rule)) ||
				((passIndex == 3) && RunOnThirdPass(rule)))
			{
				if (((postScore == 0) && RunOnZeroScore(rule)) ||
					((postScore < 0) && RunOnNegativeScore(rule)) ||
					((postScore > 0) && RunOnPositiveScore(rule)))
				{
					if (((postScore < autoModerate) && RunOnNotSpam(rule)) ||
						((postScore >= autoModerate) && (postScore < autoDelete) && RunOnPossibleSpam(rule)) ||
						((postScore >= autoDelete) && RunOnDefiniteSpam(rule)))
					{
						// Calculate rule-based spam score
						ruleScore = rule.RatePost(post, e);

						// Audit, if desired
						if (audit)
							AuditRuleScore(post, rule, ruleScore, passIndex);
					}
				}
			}

			return ruleScore;
		}

		private void AuditRuleScore(Post post, SpamRule rule, int score, int pass)
		{
			string format = "{0}:{1} [Pass #{2}];";
			string audit = string.Format(format, rule.Name, score, pass);
			post.SetExtendedAttribute("SpamScoreAudit", post.GetExtendedAttribute("SpamScoreAudit") + audit);
		}

		private const string bodyFormat = "Score: {0}\nSectionId: {1}\nUserId:{2}\nIP:{3}\nApplication:{4}\n\n{5}";
		private void Log(Post post, int score, ApplicationType atype, bool isModerated, bool audit)
		{
			CSContext context = CSContext.Current;
			string body = string.Format(bodyFormat, score, post.SectionID, context.User.UserID, post.UserHostAddress, atype.ToString(), post.Body);

			if (audit)
				body = string.Format("{0}\n\n\nSpam Score Audit\n{1}", body, post.GetExtendedAttribute("SpamScoreAudit").Replace(":", ": ").Replace(";", "\n"));

			if (isModerated)
			{
				EventLogs.Info(body, "Spam: Auto Moderate", 901, context.SettingsID);
			}
			else
			{
				EventLogs.Warn(body,"Spam: Auto Delete", 900, context.SettingsID);
			}
		}

		private int Add(int x, int y)
		{
			// handle overflow
			if ((x >= 0 && y >= 0) && (x + y < 0))
				return int.MaxValue;

			// handle underflow
			if ((x <= 0 && y <= 0) && (x + y > 0))
				return int.MinValue;

			return x + y;
		}

		private bool RunOnFirstPass(SpamRule rule)
		{
			return (rule.PassCriteria == PassCriteria.FirstPass);
		}

		private bool RunOnSecondPass(SpamRule rule)
		{
			return (rule.PassCriteria == PassCriteria.SecondPass);
		}

		private bool RunOnThirdPass(SpamRule rule)
		{
			return (rule.PassCriteria == PassCriteria.ThirdPass);
		}

		private bool RunOnZeroScore(SpamRule rule)
		{
			return ((rule.ScoreCriteria == ScoreCriteria.AnyScore) || (rule.ScoreCriteria == ScoreCriteria.ZeroScore));
		}

		private bool RunOnNegativeScore(SpamRule rule)
		{
			return ((rule.ScoreCriteria == ScoreCriteria.AnyScore) || (rule.ScoreCriteria == ScoreCriteria.NegativeScore));
		}

		private bool RunOnPositiveScore(SpamRule rule)
		{
			return ((rule.ScoreCriteria == ScoreCriteria.AnyScore) || (rule.ScoreCriteria == ScoreCriteria.PositiveScore));
		}

		private bool RunOnNotSpam(SpamRule rule)
		{
			return ((rule.StatusCriteria == StatusCriteria.AnyStatus) || (rule.StatusCriteria == StatusCriteria.NotSpam));
		}

		private bool RunOnPossibleSpam(SpamRule rule)
		{
			return ((rule.StatusCriteria == StatusCriteria.AnyStatus) || (rule.StatusCriteria == StatusCriteria.PossibleSpam));
		}

		private bool RunOnDefiniteSpam(SpamRule rule)
		{
			return ((rule.StatusCriteria == StatusCriteria.AnyStatus) || (rule.StatusCriteria == StatusCriteria.DefiniteSpam));
		}
	}
}
